package com.suyeer.fastwechat.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.suyeer.basic.util.*;
import com.suyeer.fastwechat.bean.fwpay.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.util.Date;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;

import static com.suyeer.basic.util.BasicUtil.getNonceStr;
import static com.suyeer.fastwechat.util.FwBaseUtil.checkParams;
import static com.suyeer.fastwechat.util.FwConstUtil.*;
import static org.apache.commons.codec.CharEncoding.UTF_8;

/**
 * @author jun 2018/11/22
 */
public class FwPayUtil {

    private static AtomicInteger orderIndex = new AtomicInteger(0);
    private final static int MAX_ORDER_INDEX = 999;

    /**
     * H5支付
     *
     * @param obj Object
     * @return String
     * @throws Exception Exception
     */
    public static String getH5PayMWebUrl(Object obj) throws Exception {
        FwH5PayUnifiedOrderResult fwH5PayUnifiedOrderResult = getH5PayUnifiedOrderResult(obj);
        LogUtil.info(fwH5PayUnifiedOrderResult.toString());
        checkResult(fwH5PayUnifiedOrderResult);
        return fwH5PayUnifiedOrderResult.getMweb_url();
    }

    public static FwH5PayUnifiedOrderResult getH5PayUnifiedOrderResult(Object obj) throws Exception {
        checkParams(obj);
        TreeMap<String, String> treeMap = JsonUtil.changeType(obj, TreeMap.class);
        String macKey = treeMap.remove("mch_key");
        String wxResult = unifiedOrder(treeMap, macKey);
        LogUtil.info(wxResult);
        return getBean(wxResult, FwH5PayUnifiedOrderResult.class);
    }

    /**
     * 小程序支付, 公众号支付
     *
     * @param obj Object
     * @return JSONObject
     * @throws Exception Exception
     */
    public static JSONObject getPayParams(Object obj) throws Exception {
        checkParams(obj);
        TreeMap<String, String> treeMap = JsonUtil.changeType(obj, TreeMap.class);
        String macKey = treeMap.remove("mch_key");
        String wxResult = unifiedOrder(treeMap, macKey);
        LogUtil.info(wxResult);
        FwPayBasicResult fwPayBasicResult = getBean(wxResult, FwPayBasicResult.class);
        LogUtil.info(fwPayBasicResult.toString());
        checkResult(fwPayBasicResult);
        return createPackageValue(fwPayBasicResult.getPrepay_id(), fwPayBasicResult.getAppid(), macKey);
    }

    /**
     * 检查微信返回结果
     *
     * @param fwCodeAndMsgResult FwCodeAndMsgResult
     * @throws Exception Exception
     */
    private static void checkResult(FwCodeAndMsgResult fwCodeAndMsgResult) throws Exception {
        if (!WX_RESPONSE_STATUS_SUCCESS.equalsIgnoreCase(fwCodeAndMsgResult.getReturn_code())) {
            throw new Exception(fwCodeAndMsgResult.getReturn_msg());
        }
        if (!WX_RESPONSE_STATUS_SUCCESS.equalsIgnoreCase(fwCodeAndMsgResult.getResult_code())) {
            throw new Exception(fwCodeAndMsgResult.getErr_code_des());
        }
    }

    /**
     * 企业付款
     *
     * @param obj             Object
     * @param p12CertFilePath String
     * @return String
     * @throws Exception Exception
     */
    public static String getMchPayResult(Object obj, String p12CertFilePath) throws Exception {
        TreeMap<String, String> treeMap = JsonUtil.changeType(obj, TreeMap.class);
        String macKey = treeMap.remove("mch_key");
        treeMap.put("sign", FwBaseUtil.createSign(treeMap, macKey));
        String result = WxSSLHttpPost(treeMap, p12CertFilePath);
        LogUtil.info(result);
        FwMchPayResult fwMchPayResult = getBean(result, FwMchPayResult.class);
        LogUtil.info(fwMchPayResult.toString());
        checkResult(fwMchPayResult);
        return result;
    }

    private static String unifiedOrder(TreeMap<String, String> treeMap, String mchKey) throws Exception {
        treeMap.put("sign", FwBaseUtil.createSign(treeMap, mchKey));
        return HttpResUtil.sendHttpPostRequest(WX_URL_UNIFIED_ORDER, getXmlData(treeMap));
    }

    private static JSONObject createPackageValue(String prepayId, String appId, String mchKey) {
        TreeMap<String, String> treeMap = new TreeMap<>();
        treeMap.put("appId", appId);
        treeMap.put("timeStamp", DateUtil.getTimestampString());
        treeMap.put("nonceStr", BasicUtil.getNonceStr());
        treeMap.put("package", "prepay_id=" + prepayId);
        treeMap.put("signType", "MD5");
        treeMap.put("paySign", FwBaseUtil.createSign(treeMap, mchKey));
        /*为支持 wx.chooseWXPay, 这里新写入一个timestamp参数*/
        treeMap.put("timestamp", treeMap.get("timeStamp"));
        return JsonUtil.toJSONObject(treeMap);
    }

    public static String getOrderNumber(String preFixString) {
        preFixString = StringUtils.defaultIfBlank(preFixString, StringUtils.EMPTY);
        String currTimeStr = DateUtil.getShortTimeFormStr(new Date());
        int idx = orderIndex.addAndGet(1);
        if (idx > MAX_ORDER_INDEX) {
            orderIndex.set(0);
            idx = 0;
        }
        return String.format("%s%s%03d%03d", preFixString, currTimeStr, idx, RandomUtils.nextInt(0, 1000));
    }

    public static String WxSSLHttpPost(TreeMap<String, String> treeMap, String p12CertFilePath) throws Exception {
        String mchId = treeMap.get("mchid");
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        FileInputStream is = new FileInputStream(new File(p12CertFilePath));
        keyStore.load(is, mchId.toCharArray());
        is.close();
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();
        SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslcontext, BasicUtil.createHostnameVerifier(true));
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
        HttpPost httpPost = new HttpPost(WX_URL_MCH_PAY);
        httpPost.setEntity(new StringEntity(getXmlData(treeMap), Consts.UTF_8));
        CloseableHttpResponse response = httpClient.execute(httpPost);
        HttpEntity entity = response.getEntity();
        String xmlStr = EntityUtils.toString(response.getEntity(), Consts.UTF_8);
        EntityUtils.consume(entity);
        return xmlStr;
    }

    /**
     * 查询微信订单信息
     *
     * @param fwPayNotifyResult FwPayNotifyResult
     * @param mchKey            String
     * @return String
     * @throws Exception Exception
     */
    public static String queryOrder(FwPayNotifyResult fwPayNotifyResult, String mchKey) throws Exception {
        String params = getPayOrderQueryParams(fwPayNotifyResult, mchKey);
        String resContent = HttpResUtil.sendHttpPostRequest(WX_URL_ORDER_QUERY, params);
        if (StringUtils.isBlank(resContent)) {
            throw new Exception("向微信查单失败：queryOrderParams=" + params);
        }
        return resContent;
    }

    public static FwQueryOrderResult checkPayBackNotify(InputStream is) throws Exception {
        return checkPayBackNotify(IOUtils.toString(is, UTF_8), FW_PARTNER_KEY);
    }

    public static FwQueryOrderResult checkPayBackNotify(InputStream is, String mchKey) throws Exception {
        return checkPayBackNotify(IOUtils.toString(is, UTF_8), mchKey);
    }

    public static FwQueryOrderResult checkPayBackNotify(String notifyContent) throws Exception {
        return checkPayBackNotify(notifyContent, FW_PARTNER_KEY);
    }

    public static FwQueryOrderResult checkPayBackNotify(String notifyContent, String mchKey) throws Exception {
        FwPayNotifyResult fwPayNotifyResult = getBean(notifyContent, FwPayNotifyResult.class);
        // 向微信查询订单信息的正确性
        String queryOrderResContent = queryOrder(fwPayNotifyResult, mchKey);
        FwQueryOrderResult fwQueryOrderResult = getBean(queryOrderResContent, FwQueryOrderResult.class);
        checkResult(fwQueryOrderResult);
        return fwQueryOrderResult;
    }

    private static String getPayOrderQueryParams(FwPayNotifyResult fwPayNotifyResult, String mchKey) {
        //// 准备微信查询订单的参数，并生成参数签名.
        TreeMap<String, String> treeMap = new TreeMap<>();
        treeMap.put("appid", fwPayNotifyResult.getAppid());
        treeMap.put("mch_id", fwPayNotifyResult.getMch_id());
        treeMap.put("transaction_id", fwPayNotifyResult.getTransaction_id());
        treeMap.put("out_trade_no", fwPayNotifyResult.getOut_trade_no());
        treeMap.put("nonce_str", getNonceStr());
        // 生成签名
        treeMap.put("sign", FwBaseUtil.createSign(treeMap, mchKey));
        //// 生成微信查询订单请求参数（xml格式）并返回;
        return getXmlData(treeMap);
    }

    public static String getPaySuccessNoticeReturnXmlContent() {
        return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
    }

    public static String getPayFailNoticeReturnXmlContent() {
        return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[FAIL]]></return_msg></xml>";
    }

    private static String getXmlData(Object object) {
        JSONObject obj = new JSONObject();
        obj.put("xml", object);
        return BasicUtil.json2Xml(obj.toJSONString());
    }

    private static <T> T getBean(String xmlData, Class<T> tClass) {
        JSONObject obj = JSON.parseObject(BasicUtil.xml2Json(xmlData)).getJSONObject("xml");
        return JsonUtil.changeType(obj, tClass);
    }

}
